package io.github.loserya.core.sdk.aggregate;

import io.github.loserya.core.entity.SortCondition;
import io.github.loserya.core.sdk.aggregate.base.MogoAggregate;
import io.github.loserya.core.sdk.aggregate.base.MogoFromAggregate;
import io.github.loserya.core.sdk.aggregate.base.MogoFuncAggregate;
import io.github.loserya.core.sdk.aggregate.base.MogoSelectAggregate;
import io.github.loserya.core.sdk.aggregate.base.MogoWhereAggregate;
import io.github.loserya.core.sdk.aggregate.base.column.ColumnMogoAggregate;
import io.github.loserya.core.sdk.aggregate.base.column.ColumnMogoFuncAggregate;
import io.github.loserya.core.sdk.aggregate.entity.AggregationField;
import io.github.loserya.core.sdk.aggregate.entity.MogoField;
import io.github.loserya.core.sdk.aggregate.entity.ParsedQuery;
import io.github.loserya.core.wrapper.LambdaQueryWrapper;
import io.github.loserya.hardcode.constant.EAggregate;
import io.github.loserya.hardcode.constant.ESortType;
import io.github.loserya.utils.ConvertUtil;
import io.github.loserya.utils.QueryBuildUtils;
import io.github.loserya.utils.func.SFunction;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
import org.springframework.data.mongodb.core.query.Query;

import java.util.Arrays;

public class LambdaAggregate<Q, R>
        implements
        MogoSelectAggregate<LambdaAggregate<Q, R>>,
        MogoAggregate<LambdaAggregate<Q, R>, SFunction<Q, ?>, SFunction<R, ?>>,
        ColumnMogoAggregate<LambdaAggregate<Q, R>, SFunction<Q, ?>, SFunction<R, ?>>,
        MogoFromAggregate<LambdaAggregate<Q, R>, Q>,
        MogoWhereAggregate<LambdaAggregate<Q, R>, Q>,
        MogoFuncAggregate<LambdaAggregate<Q, R>, Q, R>,
        ColumnMogoFuncAggregate<LambdaAggregate<Q, R>, Q, R> {

    private final ParsedQuery<Q, R> parsedQuery = new ParsedQuery<>();

    public ParsedQuery<Q, R> getParsedQuery() {
        return parsedQuery;
    }

    private String getField(SFunction<?, ?> column) {
        return ConvertUtil.convertToFieldName(column);
    }

    @Override
    public LambdaAggregate<Q, R> from(Class<Q> clazz) {
        parsedQuery.setClazz(clazz);
        return this;
    }

    @Override
    public LambdaAggregate<Q, R> filed(boolean condition, SFunction<Q, ?> column, SFunction<R, ?> resColum) {

        if (!condition) {
            return this;
        }
        return filed(true, getField(column), getField(resColum));

    }

    @Override
    public LambdaAggregate<Q, R> avg(boolean condition, SFunction<Q, ?> column, SFunction<R, ?> resColum) {

        if (!condition) {
            return this;
        }
        return avg(true, getField(column), getField(resColum));

    }

    @Override
    public LambdaAggregate<Q, R> count(boolean condition, SFunction<Q, ?> column, SFunction<R, ?> resColum) {

        if (!condition) {
            return this;
        }
        return count(true, getField(column), getField(resColum));

    }

    @Override
    public LambdaAggregate<Q, R> max(boolean condition, SFunction<Q, ?> column, SFunction<R, ?> resColum) {

        if (!condition) {
            return this;
        }
        return max(true, getField(column), getField(resColum));

    }

    @Override
    public LambdaAggregate<Q, R> min(boolean condition, SFunction<Q, ?> column, SFunction<R, ?> resColum) {

        if (!condition) {
            return this;
        }
        return min(true, getField(column), getField(resColum));

    }

    @Override
    public LambdaAggregate<Q, R> sum(boolean condition, SFunction<Q, ?> column, SFunction<R, ?> resColum) {

        if (!condition) {
            return this;
        }
        return sum(true, getField(column), getField(resColum));

    }

    @Override
    public LambdaAggregate<Q, R> select() {
        return this;
    }

    @Override
    public LambdaAggregate<Q, R> where(LambdaQueryWrapper<Q> lambdaQueryWrapper) {

        Query query = QueryBuildUtils.buildQuery(lambdaQueryWrapper);
        parsedQuery.setWhereClause(query);
        return this;

    }

    @Override
    @SafeVarargs
    public final LambdaAggregate<Q, R> groupBy(boolean condition, SFunction<Q, ?>... column) {

        if (!condition) {
            return this;
        }
        String[] columns = Arrays.stream(column).map(this::getField).toArray(String[]::new);
        return groupBy(true, columns);

    }

    @Override
    @SafeVarargs
    public final LambdaAggregate<Q, R> orderByDesc(boolean condition, SFunction<R, ?>... column) {

        if (!condition) {
            return this;
        }
        String[] columns = Arrays.stream(column).map(this::getField).toArray(String[]::new);
        return orderByDesc(true, columns);

    }

    @SafeVarargs
    @Override
    public final LambdaAggregate<Q, R> orderByAsc(boolean condition, SFunction<R, ?>... column) {

        if (!condition) {
            return this;
        }
        String[] columns = Arrays.stream(column).map(this::getField).toArray(String[]::new);
        return orderByAsc(true, columns);

    }

    @Override
    public LambdaAggregate<Q, R> skip(boolean condition, long skip) {

        if (condition) {
            parsedQuery.setSkip(skip);
        }
        return this;

    }

    @Override
    public LambdaAggregate<Q, R> limit(boolean condition, Long limit) {

        if (condition) {
            parsedQuery.setLimit(limit.intValue());
        }
        return this;

    }

    @Override
    public LambdaAggregate<Q, R> options(AggregationOptions options) {
        return options(true, options);
    }

    @Override
    public LambdaAggregate<Q, R> options(boolean condition, AggregationOptions options) {
        if (condition) {
            parsedQuery.options(options);
        }
        return this;
    }

    @Override
    @SafeVarargs
    public final LambdaAggregate<Q, R> groupBy(SFunction<Q, ?>... column) {
        return MogoFuncAggregate.super.groupBy(column);
    }

    @Override
    @SafeVarargs
    public final LambdaAggregate<Q, R> orderByDesc(SFunction<R, ?>... column) {
        return MogoFuncAggregate.super.orderByDesc(column);
    }

    @Override
    @SafeVarargs
    public final LambdaAggregate<Q, R> orderByAsc(SFunction<R, ?>... column) {
        return MogoFuncAggregate.super.orderByAsc(column);
    }

    @Override
    public LambdaAggregate<Q, R> filed(boolean condition, String column, String resColum) {

        if (condition) {
            parsedQuery.getFields().add(new MogoField(column, resColum));
        }
        return this;

    }

    @Override
    public LambdaAggregate<Q, R> avg(boolean condition, String column, String resColum) {

        if (condition) {
            parsedQuery.getAggregations().add(new AggregationField(EAggregate.AVG, column, resColum));
        }
        return this;

    }

    @Override
    public LambdaAggregate<Q, R> count(boolean condition, String column, String resColum) {

        if (condition) {
            parsedQuery.getAggregations().add(new AggregationField(EAggregate.COUNT, column, resColum));
        }
        return this;

    }

    @Override
    public LambdaAggregate<Q, R> max(boolean condition, String column, String resColum) {

        if (condition) {
            parsedQuery.getAggregations().add(new AggregationField(EAggregate.MAX, column, resColum));
        }
        return this;

    }

    @Override
    public LambdaAggregate<Q, R> min(boolean condition, String column, String resColum) {

        if (condition) {
            parsedQuery.getAggregations().add(new AggregationField(EAggregate.MIN, column, resColum));
        }
        return this;

    }

    @Override
    public LambdaAggregate<Q, R> sum(boolean condition, String column, String resColum) {

        if (condition) {
            parsedQuery.getAggregations().add(new AggregationField(EAggregate.SUM, column, resColum));
        }
        return this;

    }

    @Override
    public LambdaAggregate<Q, R> groupBy(boolean condition, String... column) {

        if (condition) {
            for (String filed : column) {
                parsedQuery.getGroupByField().add(filed);
            }
        }
        return this;

    }

    @Override
    public LambdaAggregate<Q, R> orderByDesc(boolean condition, String... column) {

        if (condition) {
            for (String filed : column) {
                parsedQuery.getSortConditions().add(new SortCondition(ESortType.DESC, filed));
            }
        }
        return this;

    }

    @Override
    public LambdaAggregate<Q, R> orderByAsc(boolean condition, String... column) {

        if (condition) {
            for (String filed : column) {
                parsedQuery.getSortConditions().add(new SortCondition(ESortType.ASC, filed));
            }
        }
        return this;

    }

}
